#-------------------------------------------------------------------------------
# Name:       notes_to_QAR_converter.py
# Purpose:    Convert raw notes into Question / Answer / Response format
#             Markup is ? for question
#                       ; for answer/response
#                       _ for input
#                       # for comment
#                       http ... [space] for link
#
# Authors:    John Graves <john.graves@slidespeech.com>
#
# Copyright:  (c) Slidespeech Ltd 2012
# Licence:    MIT license
#-------------------------------------------------------------------------------

from converter import Converter

def tag(question):
    if 't' in question:
        return question['t'].lower()
    else:
        return ""

def parseText(slideNumber,text):

    questionMode = False
    ruleMode = False

    # Sequence 'S' is a list of questions
    seq = []

    # Each question is a dictionary with keys of
    # n  = slide number (Int) to synchronize with slide images (Slide1, Slide2, ...)
    # c  = question counter (Int) to distiguish audio files (Slide1q1, Slide1q2, ...)
    # t  = question tag (String)
    # l  = question language (String - currently unused?)
    # q  = question text (String)
    # i  = image file (String - *.png)
    # A  = Answers (List of Answers)
    question = {'n':slideNumber}
    onQuestion = 0
    question['c'] = onQuestion

    # Each answer is a dictionary with keys of
    # a  = answer text (String)
    # s  = answer side link (String - url)
    # l  = answer language (String - currently unused? - note that l can be reused)
    # m  = answer action/movement (int)
    # r  = response text (String)
    # k  = response side link (String - reference to other Sequence)
    # f  = response language - foreign (String - currently unused?)
    # i  = input allowed (boolean)
    answer = {}

    rule = {}

    for line in text:
        line = line.strip()

        # ignore comment lines and blank lines
        if line.startswith(u"#") or len(line)==0:
            continue

        # check for lines containing [<something>]
        #    tags
        #    rules
        #    parameters [<key>=<value]
        if (line.startswith(u"[") and line.endswith(u"]")):

            # a new tag, rule or parameter ends prior question
            if 'q' in question:
                seq.append(question)
                onQuestion += 1
                question = {'n':slideNumber,'c':onQuestion}

            # question tag
            question['t'] = line[1:line.find(u"]")]
            continue

        # set question mode for any line with just ?
        if line == '?':
            questionMode = True
            if 'q' in question:
                seq.append(question)
                onQuestion += 1
                question = {'n':slideNumber,'c':onQuestion}
            continue

        if questionMode == False:
            # In default mode, expected arrangement of links and text
            # is
            #    LINK (ending in http/html/jpg)
            #    TEXT FOR VOICE OVER
            #    (optionally several lines long)
            #
            if (line.startswith(u"http") or
                line.endswith(u".html") or
                line.endswith(u".jpg") or
                line.endswith(u".JPG") or
                line.endswith(u".png") or
                line.endswith(u".PNG")):
                    if 'q' in question:
                        seq.append(question)
                        onQuestion += 1
                        question = {'n':slideNumber,'c':onQuestion}
                    question['i'] = line
            else:
                if 'q' in question:
                    question['q'] = question['q'] + '\n' + line
                else:
                    question['q'] = line

        else:
            # questionMode == True
            # In question mode, expected arrangement
            # is
            #    (optional image to show)
            #    QUESTION
            #    (optionally several lines long)
            #    LINK ANSWER ; LINK RESPONSE
            #    (optionally repeated)
            #
            # (unless ALSO in rule mode)
            if ruleMode == True:
                pass
##                # parse line for rule
##                (ruleAttribute, ruleAttributeValue) = line.split("=")
##                ruleAttribute = ruleAttribute.strip()
##                ruleAttributeValue = ruleAttributeValue.strip()
##                print (ruleAttribute, ruleAttributeValue)
##                # a regular expression overrides an example
##                if u're'==ruleAttribute:
##                    rule.rulePattern = re.compile(ruleAttributeValue, re.IGNORECASE)
##                elif u'reply'==ruleAttribute:
##                    rule.ruleResponse = ruleAttributeValue
##                elif u'example'==ruleAttribute:
##                    # turn example into a regular expression
##                    openBracketAt = ruleAttributeValue.find(u'[')
##                    closeBracketAt = ruleAttributeValue.find(u']', openBracketAt + 1)
##                    secondOpenBracketAt = ruleAttributeValue.find(u'[', closeBracketAt + 1)
##                    secondCloseBracketAt = ruleAttributeValue.find(u']', secondOpenBracketAt + 1)
##                    if openBracketAt == -1:
##                        # if no brackets, use whole example as the regular expression
##                        rule.rulePattern = re.compile(ruleAttributeValue, re.IGNORECASE)
##                    else:
##                        firstPartRE = ruleAttributeValue[ openBracketAt + 1 : closeBracketAt ].strip()
##                        rule.rulePattern = '(' + firstPartRE + ')(.*)'
##                        if openBracketAt > 0:
##                            rule.rulePattern = '(.*)' + rule.rulePattern
##                        if secondOpenBracketAt > closeBracketAt:
##                            secondPartRE = ruleAttributeValue[ secondOpenBracketAt + 1 : secondCloseBracketAt ].strip()
##                            rule.rulePattern = rule.rulePattern + '(' + secondPartRE + ')(.*)'
##                        rule.rulePattern = re.compile(rule.rulePattern, re.IGNORECASE)
            else:
                # Not in rule mode
                if (line.endswith(u".jpg") or
                    line.endswith(u".JPG") or
                    line.endswith(u".png") or
                    line.endswith(u".PNG")):
                        if len(question['q'])>0:
                            seq.append(question)
                            onQuestion += 1
                            question = {'n':slideNumber,'c':onQuestion}
                        question['i'] = line
                        continue

                else:
                    if line.startswith(u"_"):
                        answer['i'] = True
                        continue

                    # parse answer / response lines containing ;
                    semicolonAt = line.find(u";")

                    if -1 == semicolonAt:
                        if 'q' in question:
                            question['q'] = question['q'] + '\n' + line
                        else:
                            question['q'] = line
                        continue

                    else: # parse answer
                        answer['a'] = line[0:semicolonAt].strip()
                        if answer['a'].startswith(u"http"):
                            spaceAt = answer['a'].find(u" ")
                            if 1<spaceAt and spaceAt+1<len(answer['a']):
                                answer['s'] = answer['a'][0:spaceAt]
                                answer['a'] = answer['a'][spaceAt+1:]

                        responseSide = line[semicolonAt+1:].strip()
                        if len(responseSide)>0:
                            while responseSide.startswith(u";"):
                                if 'm' in answer:
                                    answer['m'] += 1
                                else:
                                    answer['m'] = 1
                                responseSide = responseSide[1:].strip()
                            if responseSide.startswith(u"http"):
                                spaceAt = responseSide.find(u" ")
                                # default
                                if 0<spaceAt:
                                   answer['k'] = responseSide[0:spaceAt]
                                   responseSide = responseSide[spaceAt+1:].strip()
                                else:
                                    answer['k'] = responseSide
                                    responseSide = ' ' # no response
                                # check for [[next script]]
                                ## FIXME
                                if answer['k'].startswith(u"["):
                                    nextScript = answer['k'][1:]
                                    if responseSide.startswith(u"]"):
                                        responseSide = responseSide[1:].strip()
                                    # turn name of next script into fetch
                                    tagForURL = urllib.urlencode({'tag':nextScript})
                                    answer['k']="/fetchScriptToView?"+tagForURL
                            answer['r'] = responseSide
                        if answer['a'] == u"[next]":
                            answer['m'] = 1
                        if 'A' in question:
                            question['A'].append(answer)
                        else:
                            question['A'] = [answer]
                        answer = {}

    if len(question['q'])>0:
        seq.append(question)
##    if len(rule.ruleName)>0:
##        seq.rules.append(rule)

    # second pass to match responseSideLinks to tags and adjust actions

    tags = [ tag(question) for question in seq ]

    for qnum, question in enumerate( seq ):
        if 'A' in question:
            for answer in question['A']:
                if 'k' in answer:
                    if answer['k'].lower() in tags:
                        # remove link
                        answer['m'] = tags.index(answer['k'].lower()) - qnum
                        answer['k'] = ""

    #dumpSequence(seq, questionMode)

    return seq

def dumpSequence(seq, questionMode):
    if True:
        f = open('output.json','w')
        f.write("""{"S":
    [
        {""")
        #f.write("test run at " + strftime("%d %b %Y %H:%M", localtime()))
        #f.write("\nquestionMode is "+ str(questionMode))
        for i, q in enumerate(seq):
            #f.write("\nQuestion "+str(i)+"-"*30)
            if 't' in q:
                f.write('\n            "t":"'+q['t']+'",')
            if 'l' in q:
                f.write('\n            "l":"en",')
            f.write('\n            "q":"'+q['q']+'",')
            if 'i' in q:
                f.write('\n            "i":"'+q['i']+'",')

            if 'A' in q:
                if len(q['A'])==0:
                    f.write('\n            "A": []')
                else:
                    f.write('\n            "A": [')
                    for j, a in enumerate(q['A']):
                        # write a new line if this is the first item
                        if j==0:
                            f.write('\n                    ')

                        #write opening brace for answer object
                        f.write('{')

                        # write answer object content
                        f.write('\n                        "a":"'+a['a']+'",')
                        if 's' in a:
                            f.write('\n                        "s":"'+a['s']+'",')
                        if 'l' in a:
                            f.write('\n                        "l":"en",')
                        if 'm' in a:
                            f.write('\n                        "m":'+str(a['m'])+',')
                        if 'r' in a:
                            f.write('\n                        "r":"'+a['r']+'",')
                        if 'k' in a:
                            f.write('\n                        "k":"'+a['k']+'",')
                        if 'f' in a:
                            f.write('\n                        "f":"en",')
                        if 'i' in a:
                            f.write('\n                        "i":'+str(a['i']).lower())

                        # write closing brace for answer object
                        f.write('\n                    }')

                        # write a comma if this isn't the last item
                        if j < len(q['A'])-1:
                            f.write(',')
                    # write closing brace for this answer
                    f.write('\n                 ]')
            # Supply braces for Question
            if i<len(seq)-1:
                f.write('\n        },{')
            #for l in q['q']:
            #    f.write("\n      questionText: "+l)
            #for j, a in enumerate(q['A']):
            #    f.write("\n       answerText"+str(j)+": "+a['a'])
            #    f.write("\n   answerSideLink"+str(j)+": "+a['s'])
            #    f.write("\n responseSideLink"+str(j)+": "+a['k'])
            #    f.write("\n     responseText"+str(j)+": "+a['r'])
            #    f.write("\n           action"+str(j)+": "+str(a['m']))
            #    f.write("\n            input"+str(j)+": "+str(a['i']))
            #f.write("\n unparsedQuestion"+str(i)+": "+str(q.unparsedQuestion))
        #for i, r in enumerate(seq.rules):
        #    f.write("\nRule "+str(i)+"-"*30)
        #    f.write("\n              name: "+r.ruleName)
        #    f.write("\n           pattern: "+str(r.rulePattern))
        #    f.write("\n              type: "+r.ruleType)
        #    f.write("\n          response: "+r.ruleResponse)

        f.write("""\n        }
    ]
}\n""")
        f.close()


def parseNotes(script):

    # input sequence
    seq_in = script['S']

    # output sequence
    seq_out = []

    for slideNumber, q in enumerate(seq_in):
        q['q'] = q['q'].replace('\r','\n')
        # process questions with multiple lines
        if "\n" in q['q']:

            # raw input
            lines_in = q['q'].split("\n")

            q_out = parseText(slideNumber,lines_in)
            for q in q_out:
                seq_out.append(q)

        else:
            # pass through, adding slideNumber
            q['n'] = slideNumber
            seq_out.append(q)

    script = {'S':seq_out}

    return script

class NotesToQARConverter(Converter):
    '''Converter to restructure raw script into Question Answer Response structure
    '''
    def __init__(self, previous=None):
        Converter.__init__(self, previous)

    def _convertImpl(self):
        try:
            seq = self.script()
            newSeq = parseNotes(seq)
            del seq['S']
            seq['S'] = newSeq['S']

        except KeyError as e:
            ex = ConverterError(_("Field missing in slide script"), e)
            raise ex


if __name__ == '__main__':
    # a simple test/demo
    c = NotesToQARConverter()
    seq = c.script()
    del seq['S']
    seq['S'] = [{'q': u' A small river with some flowers.\n?\nWhere is this city located?\nhttp://test.com In New York;;\nIn Arizona;', 'l': 'en'}, {'q': u' In New York City.', 'l': 'en'}]
    c.convert()
    print(c.script())
